iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 29
1
自我挑戰組

30天找回寫程式手感計劃!!!系列 第 29

Day29 - 「登愣登愣,登愣登登登」~ 隱挑戰 Day5

  • 分享至 

  • xImage
  •  

雖然鐵人賽已經來到倒數第二天了,
但對我來說還沒有真的要結束的感覺XD
因為這個隱挑戰看起來沒辦法趕在明天完成Q_Q
但我覺得開始了草草結束它有點可惜,
所以明天過後我應該還是會繼續寫下去XD
(PS. 今天有大大跟我說我才知道 Day30 過後還是可以 Day31, 32... 這樣寫下去XD)

https://ithelp.ithome.com.tw/upload/images/20201005/20129873Ipb6IC7Hvq.png
可能忙了又忙 可能寫了又寫 可能無數咖啡 在夜晚嚐了又嚐
可是換來成長 可是換來希望 相信有天你站在台上。

今天的 slogan 一樣很喜歡,所以還是放在文章的開頭處勉勵自己一下XD

正片開始

今日課題:重新挑戰回合制

有鑑於昨天的 code 真的寫得有夠亂,
我覺得應該要重新整理一下思緒,
重寫一下比較對得起自己的良心(?),
所幸在開始隱挑戰的時候我就已經有用 GitHub 在做版控,
所以我要將版本復原到前天的版本。
切換分支:git checkout 分支名稱
https://ithelp.ithome.com.tw/upload/images/20201005/20129873D0KOtpJDAi.png

輕輕鬆鬆就回到前天的版本:D

  1. 但這邊提醒一下,做 git checkout 時不建議開著 Prepros,
    開著 Prepros 就切過去的話,
    Prepros 會因為偵測 scss 目錄底下有異動,就去異動 CSS 檔案,
    這樣會導致舊分支有新檔案,檔案控管會有衝突。
  2. 切完後,因為要開始今天的異動,
    所以我習慣再產生一個新的分支,
    新增分支:git branch 分支名稱
    https://ithelp.ithome.com.tw/upload/images/20201005/20129873YKdghrsvS8.png

回合制的運作

檢討一下昨天比較有問題的地方:

  1. 程式的處理速度很快,即使程式有先後順序,但其實執行起來是一樣快的,一定要有先後順序就要用函數處理。
  2. 要讓程式延遲執行其實好像用 setTimeout 就好,不需要用到 Lodash 的 debounce。

試著再剖析一下整個回合的程序,應該是:
男選動作→女選動作→男攻擊→女攻擊→怪攻擊
栽在動作先後順序上,
是因為選動作是有鍵盤監聽事件的,
在按下鍵盤按鍵之後才會有後續動作
所以很好控制先後順序。
但選動作之後的攻擊是沒有鍵盤事件的,
會一連串跑完,
所以這邊只能用函數控制先後順序。

我把昨天的進度抄到選擇動作的地方,
這次要試著在 男攻擊→女攻擊 中間有延遲的效果,
終於找到救星:setTimeout(函數,延遲毫秒數)
為了試延遲效果,
先在 JavaScript 這樣寫:

currentActor = 0;
setTimeout(roleActionExecute, 1000);

function roleActionExecute(){
    console.log(`${new Date()}`);
    console.log(`開始執行動作,現在輪到 ${roleStatus[currentActor].Name}`);
    console.log(`${roleStatus[currentActor].Name} 選擇的動作是 ${roleStatus[currentActor].Action.OptionSelection}`);
    currentActor ++;
    if ( currentActor < 2 ){
        setTimeout(roleActionExecute, 2000);
    }
}

https://ithelp.ithome.com.tw/upload/images/20201005/20129873N4kNjMMhEw.png
太好了,李逍遙攻擊後有延遲 2 秒才換趙靈兒攻擊!
這邊再加上扣掉怪的血量 console.log 看一下吧:

function roleActionExecute(){
    console.log(`${new Date()}`);
    console.log(`開始執行動作,現在輪到 ${roleStatus[currentActor].Name}`);
    console.log(`${roleStatus[currentActor].Name} 選擇的動作是 ${roleStatus[currentActor].Action.OptionSelection}`);

    switch(roleStatus[currentActor].Action.OptionSelection){
        case "attack": // 動作為攻擊
            let attackQty = roleStatus[currentActor].AttackPower - roleStatus[2].DefensePower;
            roleStatus[2].HealthPoint[0] -= attackQty;
            console.log(`現在 ${roleStatus[2].Name} 的血量為 ${roleStatus[2].HealthPoint[0]}`);
            break;
        default:
            console.log("default");
            break;
    }
    currentActor ++;
    if ( currentActor < 2 ){
        setTimeout(roleActionExecute, 2000);
    }
}

https://ithelp.ithome.com.tw/upload/images/20201005/201298736mFLwXEhRv.png
看起來 OK 之後,再讓我們把扣血的顯示加回去吧!

扣血的顯示

這邊就可以用 setTimeout(函數,延遲毫秒數) 輕鬆達到延遲的效果。

function roleActionExecute(){
    console.log(`${new Date()}`);
    console.log(`開始執行動作,現在輪到 ${roleStatus[currentActor].Name}`);
    console.log(`${roleStatus[currentActor].Name} 選擇的動作是 ${roleStatus[currentActor].Action.OptionSelection}`);
    
    if ( currentActor < 2 ){
        switch(roleStatus[currentActor].Action.OptionSelection){
            case "attack": // 動作為攻擊
                let attackQty = roleStatus[currentActor].AttackPower - roleStatus[2].DefensePower;
                roleStatus[2].HealthPoint[0] -= attackQty;
                monsterBloodMinusElement.textContent = `-${attackQty}`;
                console.log(`現在 ${roleStatus[2].Name} 的血量為 ${roleStatus[2].HealthPoint[0]}`);
                
                let newClass = `${originClass} animate__animated animate__flash`;
                monsterBloodMinusElement.setAttribute("class",newClass);
                setTimeout(bloodClassReset, 2000); // 延遲2秒才將 Animate 的 class 拿掉
                break;
            default:
                console.log("default");
                break;
        }
        // 如果怪的血量 <= 0,表示怪被消滅了,後續動作不用執行
        if ( roleStatus[2].HealthPoint[0] <=0 ){
            console.log("戰鬥勝利");
        } else{
            currentActor ++;
            setTimeout(roleActionExecute, 3000); // 延遲 3 秒再執行一次角色執行動作
        }        
    } else{
        console.log("輪到怪");
    }
    
}

function bloodClassReset(){
    console.log(`${new Date()}`);
    console.log("清空 Animate.css 的 class");
    monsterBloodMinusElement.textContent = "";
    monsterBloodMinusElement.setAttribute("class",originClass);
}

https://ithelp.ithome.com.tw/upload/images/20201005/20129873CcJ4D7yWO5.png
而且我是將角色執行動作都寫在同一個函數之中,
只是用 setTimeout(roleActionExecute, 3000) 等待 3 秒才換下一個角色動作這樣。

血條的顯示就跟昨天一樣,
就不贅述XD

不過寫到這邊好開心啊,
因為我總算是改變昨天一個角色一個函數的寫法,
讓我回想起 Day20 的挑戰,也是到 Day21 改變寫法XD

輪到怪攻擊→被攻擊對象要被扣血→如果怪的血>0則要繼續回到主角選擇動作的回合

首先在 roleActionExecute() 裡面寫主角扣血(先寫死固定攻擊趙靈兒),
在趙靈兒被怪攻擊時血量也會加上閃爍的動畫,
再來 bloodClassReset() 會將趙靈兒的血條回復成原本的 class,
一定要 reset 成原本的 class,
因為 Animate.css 所加的 CSS class 要在加的當下才會有動畫,
動畫結束之後則靜止不動,
所以要將為了動畫所加的 class 清掉,
下次要顯示動畫時再加一次 class。

另外還要判斷怪的血是否 >0,
是的話則將 currentActor 設回 0,(0:李逍遙 1:趙靈兒 2:怪)
再執行主角選動作的函數 roleActionSelect()

function roleActionExecute(){
    if ( currentActor < 2 ){
        ... 中略 ...      
    } else{
        console.log("輪到怪");
        let attackQty = roleStatus[currentActor].AttackPower - roleStatus[1].DefensePower;
        roleStatus[1].HealthPoint[0] -= attackQty;
        console.log(`現在 ${roleStatus[1].Name} 的血量為 ${roleStatus[1].HealthPoint[0]}`);
        girlStatusElement.innerHTML = `<p class="HP">${roleStatus[1].HealthPoint[0]}/${roleStatus[1].HealthPoint[1]}</p>`;
        girlStatusElement.innerHTML += `<p class="MP">${roleStatus[1].MagicPoint[0]}/${roleStatus[1].MagicPoint[1]}</p>`;
        // originClass = girlStatusElement.getAttribute("class");
        originClass = girlStatusElement.childNodes[0].getAttribute("class");
        console.log(`原本的 class: ${originClass}`);
        newClass = `${originClass} active animate__animated animate__flash`;
        girlStatusElement.childNodes[0].setAttribute("class",newClass);
        setTimeout(bloodClassReset, 2000); // 延遲2秒才將 Animate 的 class 拿掉
    }
}

function bloodClassReset(){
    if ( currentActor < 2 ){
        monsterBloodMinusElement.textContent = "";
        monsterBloodMinusElement.setAttribute("class",originClass);
        currentActor ++;
    } else{
        girlStatusElement.childNodes[0].setAttribute("class",originClass);
        // 如果怪的血量 > 0 則要繼續戰鬥
        if ( roleStatus[2].HealthPoint[0] >0 ){ 
            currentActor = 0; // 0:李逍遙 1:趙靈兒 2:怪 與roleStatus順序一致
            roleActionSelect();
        }
    }
}
  1. 第一回合
    主角攻擊
    https://ithelp.ithome.com.tw/upload/images/20201005/20129873Ydtr71O488.png
    怪攻擊
    https://ithelp.ithome.com.tw/upload/images/20201005/20129873Rd0lEieu2P.png
  2. 第二回合
    主角攻擊
    https://ithelp.ithome.com.tw/upload/images/20201005/20129873R3jIUF7FCh.png
  3. 怪的血 <=0,結束戰鬥
    https://ithelp.ithome.com.tw/upload/images/20201005/20129873mdVrhsOSLj.png

終於寫完整個回合制了!!!!!
開心!

[後記]

因為招式顯示應該也會花不少時間,
為了明天團隊的完賽,
明天進度暫定為箭頭顯示好了XD

我說的是輪到誰選擇動作時,
頭上會有箭頭顯示,如下方紅框所示↓
https://ithelp.ithome.com.tw/upload/images/20201005/20129873FSoCi7geIa.png
但因為我沒有把兩位主角特別切出來,
所以我會把箭頭做在右下角角色狀態那裡。

[目前進度整理]

  1. 角色血量、法力、攻擊力、防禦力、招式、當回合選擇動作 → 存在 roleStatus
  2. 目前做動角色 → currentActor
  3. 整個流程規劃:roleActionSelect(角色選擇動作),選擇完成則 currentActor +1 → currentActor 變成 2 則開始 roleActionExecute(角色執行動作) → bloodClassReset(被攻擊的血條 class reset) → 如果怪的血 >0 則 currentActor 再設為 0,再執行 roleActionSelect(角色選擇動作)
    (結束條件為 怪的血 <=0)

上一篇
Day28 - 「登愣登愣,登愣登登登」~ 隱挑戰 Day4
下一篇
Day30 - 「登愣登愣,登愣登登登」~ 隱挑戰 Day6 暨鐵人賽完賽心得
系列文
30天找回寫程式手感計劃!!!36
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言